/*
 * BlackLightning.PluginLib.Modules.Packets.StormFront.SFCommandPacket
 * Andy Tidball
 * 
 * Project: Black Lightning Plugin Library
 * Copyright: GNU General Public License
 */

using System;
using System.Collections.Generic;
using System.Text;
using BlackLightning.PluginLib.Modules.Packets;

namespace BlackLightning.PluginLib.Modules.Packets.StormFront {
    /// <summary>
    /// The StormFront implementation of CommandPacket.
    /// </summary>
    [Serializable]
    public class SFCommandPacket : CommandPacket {
        /// <summary>
        /// Parses an SFCommandPacket from a string of data.
        /// </summary>
        /// <param name="Source">The program that the data came from.</param>
        /// <param name="Data">The data to parse.</param>
        /// <returns>An SFCommandPacket created from the given data, or null if the data cannot be parsed.</returns>
        public static SFCommandPacket Parse(Program Source, string Data) {
            // trim the whitespace
            string Remainder = Data.Trim();

            // make sure the Remainder is long enough
            if (Remainder.Length < 4) {
                return null;
            }

            // make sure there's a command tag
            if (!Remainder.StartsWith("<c>")) {
                return null;
            }

            // remove the command tag
            Remainder = Remainder.Substring(3);

            // split up the Remainder into a command and arguments
            string[] Split = Remainder.Split(' ');
            string Command = Split[0];

            // default destination is the game server
            Program Destination = Program.GameServer;

            // check if this is a BL command
            if (Command.StartsWith("@")) {
                // chop off the @ and set our destination
                Command = Command.Substring(1);
                Destination = Program.BlackLightning;
            }

            // run through the arguments
            List<string> Args = new List<string>();
            for (int i = 1; i < Split.Length; ++i) {
                if (Split[i].StartsWith("\"")) {
                    // if this argument starts with a quote, it doesn't end until the next argument that ends with a quote
                    string Arg = "";

                    // keep building up the argument until we end with a quote or we're out of text
                    while (i < Split.Length && !Arg.EndsWith("\"")) {
                        if (Arg != "") {
                            Arg += " ";
                        }
                        Arg += Split[i];
                        ++i;
                    }
                    --i; // negate the ++i that will happen at the end of the outer loop, since we already incremented it

                    // remove the beginning quote
                    Arg = Arg.Substring(1);

                    // remove the trailing quote
                    if (Arg.EndsWith("\"")) {
                        Arg = Arg.Substring(0, Arg.Length - 1);
                    }

                    // add the argument to the list
                    Args.Add(Arg);
                } else {
                    // add this argument to the list
                    Args.Add(Split[i]);
                }
            }

            // create and return the packet
            return new SFCommandPacket(Source, Destination, Data, Command, Args.ToArray());
        }

        /// <summary>
        /// Creates a new SFCommandPacket.
        /// </summary>
        /// <param name="Source">The program that the packet came from.</param>
        /// <param name="Destination">The program that the packet will be delivered to.</param>
        /// <param name="Command">The command to contain in the packet.</param>
        /// <param name="Arguments">The arguments given to the command contained in the packet.</param>
        public SFCommandPacket(Program Source, Program Destination, string Command, string[] Arguments)
            : this(Source, Destination, null, Command, Arguments) {
        }

        /// <summary>
        /// Creates a new SFCommandPacket.
        /// </summary>
        /// <param name="Source">The program that the packet came from.</param>
        /// <param name="Destination">The program that the packet will be delivered to.</param>
        /// <param name="Data">The data string contained in the packet.</param>
        /// <param name="Command">The command to contain in the packet.</param>
        /// <param name="Arguments">The arguments given to the command contained in the packet.</param>
        private SFCommandPacket(Program Source, Program Destination, string Data, string Command, string[] Arguments)
            : base(Source, Destination, Data, Command, Arguments) {
        }

        /// <summary>
        /// Retrieves the packet's data.
        /// </summary>
        /// <returns>The packet's data string.</returns>
        public override string CreateOutput() {
            // start with a command tag
            StringBuilder Output = new StringBuilder("<c>");

            // if this is destined for Black Lightning, add the @
            if (this.Destination == Program.BlackLightning) {
                Output.Append("@");
            }

            // append the command itself
            Output.Append(this.Command);

            // run through and append each argument
            foreach (string Arg in this.Arguments) {
                Output.Append(" ");
                if (Arg.Contains(" ")) {
                    Output.AppendFormat("\"{0}\"", Arg);
                } else {
                    Output.Append(Arg);
                }
            }

            // return the result
            return Output.ToString();
        }

        /// <summary>
        /// Copy constructor.
        /// </summary>
        /// <param name="P">The SFCommandPacket to create a copy of.</param>
        /// <param name="DeepCopyOrNextState">True to make a deep copy of the given packet, false to instead use the given packet as the new packet's previous state.</param>
        protected SFCommandPacket(CommandPacket P, bool DeepCopyOrNextState)
            : base(P, DeepCopyOrNextState) {
        }

        /// <summary>
        /// Creates a deep copy of the packet.
        /// </summary>
        /// <returns>A deep copy of the packet.</returns>
        public override object Clone() {
            return new SFCommandPacket(this, true);
        }

        /// <summary>
        /// Creates a new SFCommandPacket that has this SFCommandPacket as its PreviousState.
        /// </summary>
        /// <returns>A new SFCommandPacket that has this one as its PreviousState.</returns>
        internal override BLPacket GetNextState() {
            return new SFCommandPacket(this, false);
        }
    }
}
